package self.zhangkang.chart.service.index

import self.zhangkang.chart.data.BIAS
import self.zhangkang.chart.data.KLineInfo

/**
 * @author : Android-张康
 * created on: 2023/8/25 11:42
 * description: BIAS 指标
 *
 * 乖离率(BIAS)是由移动平均原理派生出来的一项技术指标，其功能主要是通过测算股价在波动过程中与移动平均线
 * 出现偏离的程度，从而得出股价在剧烈波动时因偏离移动平均趋势而造成的可能回档或反弹
 *
 * N日BIAS=(当日收盘价-N日移动平均价)/N日移动平均价×100
 */
class BIASTools<T>(private val N: Int, private val getClosePrice: (T) -> Float) {


    companion object {

        private const val BIAS_6 = 6

        private const val BIAS_12 = 12

        private const val BIAS_24 = 24

        fun calculationBIAS(dataList: MutableList<KLineInfo>) {
            val getClosePrice: (KLineInfo) -> Float = {
                it.closePrice
            }
            val bias6Tools = BIASTools(BIAS_6, getClosePrice)
            val bias12Tools = BIASTools(BIAS_12, getClosePrice)
            val bias24Tools = BIASTools(BIAS_24, getClosePrice)
            dataList.forEachIndexed { index, kLineInfo ->
                val bias6 = bias6Tools.calculation(dataList, index)
                val bias12 = bias12Tools.calculation(dataList, index)
                val bias24 = bias24Tools.calculation(dataList, index)
                dataList[index] = kLineInfo.copy(
                    bias = BIAS(bias6 = bias6, bias12 = bias12, bias24 = bias24)
                )
            }
        }
    }


    fun calculation(dataList: List<T>, index: Int): Float {
        if (index < N) {
            return Float.NaN
        }
        val closePrice = getClosePrice(dataList[index])
        val ma = calculateMovingAverage(dataList, index, getClosePrice)
        return (closePrice - ma) / ma * 100
    }

    /**
     * 计算移动平均值
     *
     * @param dataList K线数据列表
     * @param currentIndex 当前的位置
     *
     * @return 返回 [currentIndex] 位置的 [N] 日移动平均值
     */
    fun <T> calculateMovingAverage(
        dataList: List<T>,
        currentIndex: Int,
        getValue: (T) -> Float
    ): Float {
        var n = N
        var start = currentIndex - N + 1
        if (start < 0) {
            start = 0
            n = currentIndex
        }
        var value = 0F
        for (index in start..currentIndex) {
            value += getValue(dataList[index])
        }
        return value.div(n)
    }
}